/*
 * Copyright 2025 arisgo@163.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.arisgo.cloud.web.configurer.resolve;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.method.MethodValidationException;
import org.springframework.web.ErrorResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
import org.springframework.web.util.WebUtils;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 暂时没用到
 * 自定义全局异常处理类
 *
 * @author Arisgo
 * @since 2024/6/7
 */
@Deprecated
public class ArisgoGlobalExceptionResolver extends DefaultHandlerExceptionResolver {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    private final ObjectMapper mapper = new ObjectMapper();

    /**
     * response中的值一经设置，不易修改，设置的时候注意顺序
     *
     * @param request  current HTTP request
     * @param response current HTTP response
     * @param handler  the executed handler, or {@code null} if none chosen at the time
     *                 of the exception (for example, if multipart resolution failed)
     * @param ex       the exception that got thrown during handler execution
     */
    @Override
    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        PrintWriter writer = null;

        try {
            // 处理响应头，主要设置响应状态
            handlerResponseHeaders(request, response, ex);
            // 响应数据
            Map<String, Object> data = new LinkedHashMap<>();
            data.put("code", response.getStatus());
            data.put("msg", ex.getMessage());
            data.put("path", request.getRequestURI());
            data.put("timestamp", System.currentTimeMillis());

            writer = response.getWriter();
            mapper.writeValue(writer, data);

            return new ModelAndView();
        } catch (IOException e) {
            logger.error("global exception resolve error: {}", e.getMessage());
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
        return null;
    }

    private static void handlerResponseHeaders(HttpServletRequest request, HttpServletResponse response, Exception ex) {
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");

        if (ex instanceof ErrorResponse error) {
            if (!response.isCommitted()) {
                HttpHeaders headers = error.getHeaders();
                headers.forEach((name, values) -> values.forEach(value -> response.addHeader(name, value)));
                response.setStatus(error.getStatusCode().value());
            }
        } else if (ex instanceof ConversionNotSupportedException) {
            request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex);
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        } else if (ex instanceof HttpMessageNotReadableException) {
            if (!response.isCommitted()) {
                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            }
        } else if (ex instanceof HttpMessageNotWritableException) {
            if (!response.isCommitted()) {
                request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex);
                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            }
        } else if (ex instanceof MethodValidationException) {
            request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex);
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        } else {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    }

}
